home *** CD-ROM | disk | FTP | other *** search
- Path: ix.netcom.com!netnews
- From: jlilley@ix.netcom.com (John Lilley)
- Newsgroups: comp.lang.c++
- Subject: Re: Virtual Base Class
- Date: 14 Mar 1996 05:54:17 GMT
- Organization: Netcom
- Message-ID: <4i8ca9$57m@ixnews3.ix.netcom.com>
- References: <313F98D0.102E@ucla.edu> <4i1k92$3n8@apoll.informatik.uni-bonn.de> <4i49os$20j@apoll.informatik.uni-bonn.de>
- NNTP-Posting-Host: den-co20-12.ix.netcom.com
- Mime-Version: 1.0
- Content-Type: Text/Plain; charset=US-ASCII
- X-NETCOM-Date: Wed Mar 13 9:54:17 PM PST 1996
- X-Newsreader: WinVN 0.99.7
-
- In article <4i49os$20j@apoll.informatik.uni-bonn.de>, schregle@Mars says...
- >Yep, I'm replying to myself...
- >Roland Schregle (schregle@zeus.informatik.uni-bonn.de) wrote:
- >: Dennis Rahaman (dennisr@ucla.edu) wrote:
- >: : This is what I want to do:
- >
- >: : a
- >: : / \
- >: : b c
- >: : \ /
- >: : d
-
- [... class derivation details omitted ... ]
-
- >: : void f ()
- >: : {
- >: : a a1;
- >: : d* pd = (d*) &a1; // error: can't cast virtual base to derived
- >: : }
- >
- >: : ///////////////////////////////////////////////////////////
- >: : Can someone explain why I can't cast a virtual base class to a derived
- >: : class?
-
-
- The previous posters, while correctly deploring the concept of
- "downcasting" from a base class pointer to a derived class, failed
- to state clearly why a *virtual* base class is so much different and
- intractible than a non-virtual base class. After all, dynamic casting
- has been added to C++ to handle the non-virtual case, why couldn't
- it do the virtual case as well?
-
- Subsequent posters' attempts to perform the cast may work but are
- wrong, such as the one the gets the compiler to shut up
- buts yields the wrong value:
-
- >: d* pd = (d*)(void*)&a1;
-
- Or the one that gets the compiler to shut up and yields the
- right value by the trick of finding the differences between
- the two pointers when you cast a derived to a virtual base.
- But even this won't work all of the time (I'll explain later):
-
- > a a1;
- > d* testy = new d;
- > int offset = (void*)testy - (void*)(a*)testy;
- > delete testy;
- > d* pd = (d*)((void*)a1 + offset);
-
-
- The key concept is that virtual base classes are just like virtual
- methods in that the physical address is not known until an actual object
- is encountered at runtime. Runtime. Get it? The reason is that
- since there can only be one virtual base class contained in a derived
- object, regardless of how many times it is encountered in the derivation
- tree, the compiler must be able to change the offset between the
- virtual base class and any other class in the tree. In other
- words, the offset between "testy" and "(a*)testy" may change
- if testy points to a subclass of d instead of an actual d. Since it
- is perfectly and always valid to use pointers to derived classes anywhere
- you have a pointer to a base, then the above offset computation may fail
- if you derive from d and start using pointers to that subclass intermixed
- with pointers to d, which is, after all, what OOP is about.
-
- The above examples do not really state the problem correctly, because they
- start with real a's and d's as opposed to pointers. Let's look
- at a more realistic example (still assuming the above class hierarchy),
- and assume that the above computed offset was computed somewhere and
- squirreled away:
-
- void func1(d* d1)
- {
- a* a1 = d1;
- ...
- // somehow "know" that a1 is really a d*
- d* d2 = (d*)((void*)a1 + offset);
- d2->d_method();
- }
-
- Now consider:
-
- class e : public a
- {
- ...
- };
-
- class f : public d, public e
- {
- ...
- };
-
- Which gives a derivation tree like:
-
- a
- / | \
- / | \
- b c e
- \ / /
- d /
- \ /
- f
-
- void func2()
- {
- d d1;
- f f1;
- func1(&d1); // syntactically valid and probably works
- func1(&f1); // syntactically valid and almost certainly fails
- }
-
- In the above case, even though both d1 and f1 are "really" d's,
- the offset between the 'd' part and the 'a' part is not necessarily
- the same because of the compiler's freedom to place the single 'a'
- anywhere relative to the other class parts.
-
- The worst result of the above attempts is that they might work until
- someone uses a derived class, and then they will fail in a mysterious
- way, which even when detected will leave the poor maintenance programmer
- with nowhere to go but redesigning the code from scratch.
-
- Moral: Stop attempting to defeat the compiler and try a simple approach.
-
- John Lilley
- Nerds for Hire, Inc.
-
-